package com.swingson.componentprocessor;

import com.swingson.Utils;
import com.swingson.exceptions.SwingsonException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author sad
 */
public class ComponentProcessorResolver {

    private final Map<String, BaseProcessor> processors = new HashMap<String, BaseProcessor>();

    public BaseProcessor getProcessorForClass(Class clazz) {
        List<Class> classHierarchy = new ArrayList<>();
        Class tClass = clazz;
        while (tClass != null) {
            classHierarchy.add(tClass);
            tClass = tClass.getSuperclass();
        }

        Collections.reverse(classHierarchy);

        BaseProcessor bestProcessor = null;
        int hierarchyPosition = -1;
        Class classToFind = clazz;
        for (BaseProcessor processor : processors.values()) {
            clazz = classToFind;
            Class[] processedClass = processor.getProcessingClass();
            while (clazz != null) {
                for (Class pc : processedClass) {
                    if (Utils.isChild(clazz, pc)) {
                        int currentPosition = classHierarchy.indexOf(pc);
                        if (currentPosition > hierarchyPosition) {
                            bestProcessor = processor;
                            hierarchyPosition = currentPosition;
                        }
                        break;
                    }
                }

                clazz = clazz.getSuperclass();
            }
        }

        return bestProcessor;
    }

    public ComponentProcessorResolver addComponentProcessor(String componentLabel, BaseProcessor processor) {
        if (componentLabel == null || componentLabel.trim().isEmpty()) {
            throw new SwingsonException("Cannot add " + processor + " because componentLabel cannot be empty");
        }

        if (processors.containsKey(componentLabel)) {
            throw new SwingsonException("Such componentLabel " + componentLabel + " already assigned to " + processors.get(componentLabel));
        }

        processors.put(componentLabel, processor);
        return this;
    }

    public BaseProcessor getProcessorOrNull(String label) {
        return processors.get(label);
    }

    public BaseProcessor getProcessor(String label) {
        BaseProcessor processor = processors.get(label);
        if (processor != null) {
            return processor;
        } else {
            //try to find most appropriate
            String minDistStr = "";
            int minDist = 9999999;
            for (String key : processors.keySet()) {
                int dist = Utils.getLevenshteinDistance(key, label);
                if (dist < minDist) {
                    minDistStr = key;
                    minDist = dist;
                }
            }

            throw new SwingsonException("Cannot find processor for component '" + label + "'. Maybe you need '" + minDistStr + "'?");
        }
    }
}
